package com.atguigu.gmall.order.service;

import com.atguigu.core.bean.Resp;
import com.atguigu.core.exception.OrderException;
import com.atguigu.gmall.cart.pojo.Cart;

import com.atguigu.gmall.oms.entity.OrderEntity;
import com.atguigu.gmall.order.fegin.*;
import com.atguigu.gmall.order.interceptors.LoginInterceptor;
import com.atguigu.gmall.order.vo.OrderConfirmVO;
import com.atguigu.gmall.oms.vo.OrderItemVO;
import com.atguigu.gmall.oms.vo.OrderSubmitVO;
import com.atguigu.gmall.order.vo.UserInfo;
import com.atguigu.gmall.pms.entity.SkuInfoEntity;
import com.atguigu.gmall.pms.entity.SkuSaleAttrValueEntity;
import com.atguigu.gmall.ums.entity.MemberEntity;
import com.atguigu.gmall.ums.entity.MemberReceiveAddressEntity;
import com.atguigu.gmall.wms.entity.WareSkuEntity;
import com.atguigu.gmall.wms.vo.SkuLockVO;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
public class OrderService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    //六个client
    @Autowired
    private GmallPmsClient pmsClient;

    @Autowired
    private GmallOmsClient omsClient;

    @Autowired
    private GmallCartClient cartClient;
    @Autowired
    private GmallUmsClient  umsClient;

    @Autowired
    private GmallSmsClient smsClient;
    @Autowired
    private GmallWmsClient wmsClient;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    //消息队列
    @Autowired
    private AmqpTemplate amqpTemplate;

    private static final String TOKEN_PREFIX="order:token:";

    public OrderConfirmVO confirm() {

        OrderConfirmVO orderConfirmVO=new OrderConfirmVO();

        UserInfo userInfo= LoginInterceptor.getUserInfo();
        Long userId = userInfo.getId();
        if(userId==null){
            return null;
        }
        //异步编排
        //List<CompletableFuture> futures=new ArrayList<>();
        //1\无返回值
        CompletableFuture<Void> addressCompletableFuture = CompletableFuture.runAsync(() -> {
            //获取用户的收获地址列表,根据用户的ID查询用户的收获地址
            Resp<List<MemberReceiveAddressEntity>> addressResp = this.umsClient.queryAddressByUserId(userId);
            List<MemberReceiveAddressEntity> memberReceiveAddressEntities = addressResp.getData();
            orderConfirmVO.setAddresses(memberReceiveAddressEntities);
        }, threadPoolExecutor);
        //futures.add(addressCompletableFuture);

        //获取购物车中选中的商品信息，有返回值
        CompletableFuture<Void> bigSkuCompletableFuture = CompletableFuture.supplyAsync(() -> {
            Resp<List<Cart>> cartResp = this.cartClient.queryCheckCartsByUserId(userId);
            List<Cart> cartList = cartResp.getData();
            if (CollectionUtils.isEmpty(cartList)) {
                throw new OrderException("请勾选购物车商品");
            }
            return cartList;
        }, threadPoolExecutor).thenAcceptAsync(cartList -> {
            List<OrderItemVO> itemVOS = cartList.stream().map(
                    cart -> {
                        OrderItemVO orderItemVO = new OrderItemVO();
                        Long skuId = cart.getSkuId();

                        //2、异步编排
                        //动态实时获取最新数据
                        CompletableFuture<Void> skuCompletableFuture = CompletableFuture.runAsync(() -> {
                            Resp<SkuInfoEntity> skuInfoEntityResp = this.pmsClient.querySkuBySkuId(cart.getSkuId());
                            SkuInfoEntity skuInfoEntity = skuInfoEntityResp.getData();
                      if (skuInfoEntity != null) {
                                orderItemVO.setWeight(skuInfoEntity.getWeight());
                                orderItemVO.setDefaultImgage(skuInfoEntity.getSkuDefaultImg());
                                orderItemVO.setPrice(skuInfoEntity.getPrice());
                                orderItemVO.setTitle(skuInfoEntity.getSkuTitle());
                                orderItemVO.setSkuId(cart.getSkuId());
                                orderItemVO.setCount(cart.getCount());
                            }
                        }, threadPoolExecutor);
                        //futures.add(skuCompletableFuture);
                        //3\销售属性
                        CompletableFuture<Void> saleAttrCompletableFuture = CompletableFuture.runAsync(() -> {
                            Resp<List<SkuSaleAttrValueEntity>> skuSaleAttrValueBySkuId = this.pmsClient.querySkuSaleAttrValueBySkuId(cart.getSkuId());
                            List<SkuSaleAttrValueEntity> skuSaleAttrValueEntities = skuSaleAttrValueBySkuId.getData();
                            orderItemVO.setSaleAttrValueEntityList(skuSaleAttrValueEntities);
                        }, threadPoolExecutor);
                        //futures.add(saleAttrCompletableFuture);

                        //4\库存
                        CompletableFuture<Void> wareSkuCompletableFuture = CompletableFuture.runAsync(() -> {
                            Resp<List<WareSkuEntity>> queryWareSkuBySkuId = this.wmsClient.queryWareSkuBySkuId(cart.getSkuId());
                            List<WareSkuEntity> wareSkuEntities = queryWareSkuBySkuId.getData();
                            if (!CollectionUtils.isEmpty(wareSkuEntities)) {
                                //不为空
                                orderItemVO.setStore(wareSkuEntities.stream().anyMatch(wareSkuEntity -> wareSkuEntity.getStock() > 0));
                            }
                        }, threadPoolExecutor);

                        CompletableFuture.allOf(skuCompletableFuture, saleAttrCompletableFuture, wareSkuCompletableFuture).join();
                        return orderItemVO;
                    }

            ).collect(Collectors.toList());
            orderConfirmVO.setOrderItems(itemVOS);


            //查询用户信息、获取积分，根据ID,查询用户信息
            Resp<MemberEntity> memberEntityResp = this.umsClient.queryMemberById(userId);
            MemberEntity memberEntity = memberEntityResp.getData();
            orderConfirmVO.setBounds(memberEntity.getIntegration());



        }, threadPoolExecutor);

        //查询用户积分
        CompletableFuture<Void> memberCompletableFuture = CompletableFuture.runAsync(() -> {
            Resp<MemberEntity> memberEntityResp = this.umsClient.queryMemberById(userId);
            MemberEntity memberEntity = memberEntityResp.getData();
            orderConfirmVO.setBounds(memberEntity.getIntegration());
        }, threadPoolExecutor);

        CompletableFuture<Void> tokenCompletableFuture = CompletableFuture.runAsync(() -> {
            //生成一个唯一标志，防止重复提交（响应到页面有一份，有一份保存到Redis中）
            String orderToken = IdWorker.getIdStr();
            orderConfirmVO.setOrderToken(orderToken);
            this.redisTemplate.opsForValue().set(TOKEN_PREFIX+orderToken,orderToken);
        }, threadPoolExecutor);

        CompletableFuture.allOf(addressCompletableFuture,bigSkuCompletableFuture,memberCompletableFuture,tokenCompletableFuture).join();//在返回之前阻塞一下
        return orderConfirmVO;
    }

    /**
     * 提交订单
     *
     * @param submitVO
     */
    public OrderEntity submit(OrderSubmitVO submitVO) {

        UserInfo userInfo = LoginInterceptor.getUserInfo();
        //获取orderToken
        String orderToken = submitVO.getOrderToken();

        //1、防重，查询redis中有没有orderToken信息，有，则是第一次提交并放行，并删除redis中的OrderToken
        String script="if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Long flag = this.redisTemplate.execute(new DefaultRedisScript<>(script,Long.class), Arrays.asList(TOKEN_PREFIX + orderToken), orderToken);
        if(flag==0){
            throw new OrderException("订单不可重复提交");
        }

        //2、校验价格,总价一致，就放行，
        List<OrderItemVO> items = submitVO.getItems();//送货清单
        BigDecimal totalPrice = submitVO.getTotalPrice();//总价
        if(CollectionUtils.isEmpty(items)){
            throw new OrderException("没有购买的商品，请到购物车中勾选商品");
        }
        //实时价格
        BigDecimal currentTotalPrice = items.stream().map(item -> {
            //获取每一个价格
            Resp<SkuInfoEntity> skuInfoEntityResp = this.pmsClient.querySkuBySkuId(item.getSkuId());
            SkuInfoEntity skuInfoEntity = skuInfoEntityResp.getData();
            if (skuInfoEntity != null) {
                return skuInfoEntity.getPrice().multiply(new BigDecimal(item.getCount()));
            }
            return new BigDecimal(0);
        }).reduce((a, b) -> a.add(b)).get();//reduce计算总价格
        //判断实时价格和页面的总价是否一致
        if(currentTotalPrice.compareTo(totalPrice)!=0){
            throw new OrderException("页面已过期，请刷新页面后重试!");
        }
        //3、校验库存并锁定库存，充足就放行，一次性提示所有库存不足的商品
        //遍历送货清单
        List<SkuLockVO> lockVOS=items.stream().map(orderItemVO -> {
                SkuLockVO skuLockVO=new SkuLockVO();
                skuLockVO.setSkuId(orderItemVO.getSkuId());
                skuLockVO.setCount(orderItemVO.getCount());
                skuLockVO.setOrderToken(orderToken);//传递过去orderToken
                return skuLockVO;
        }
        ).collect(Collectors.toList());
        Resp<Object> wareResp = this.wmsClient.checkAndLockStore(lockVOS);
        if(wareResp.getCode()!=0){
            //锁定失败
            throw new OrderException(wareResp.getMsg());
        }

        //4、下单,订单表和订单列表需要插入数据
        Resp<OrderEntity> orderEntityResp=null;
        try {
            submitVO.setUserId(userInfo.getId());
            orderEntityResp = this.omsClient.saveOrder(submitVO);
            OrderEntity orderEntity = orderEntityResp.getData();
        }catch (Exception e){
            e.printStackTrace();
            //这里Seate的性能较低，所以使用最终一致性，
            //下单失败后，应该发送消息给Pms,解锁相应的库存,可以使用同一个交换机,ordertoken在锁定的时候就应该加上锁定信息

            this.amqpTemplate.convertAndSend("GMALL-ORDER-EXCHANGE","stock.unlock",orderToken);
            throw new OrderException("服务器错误，创建订单失败");
        }


        //5、删除购物车

        //发送消息
        Map<String,Object> map=new HashMap<>();
        map.put("userId",userInfo.getId());
        List<Long> skuIds = items.stream().map(OrderItemVO::getSkuId).collect(Collectors.toList());
        map.put("skuIds",skuIds);
        this.amqpTemplate.convertAndSend("GMALL-ORDER-EXCHANGE","cart.delete",map);
        if(orderEntityResp!=null){
            return orderEntityResp.getData();
        }
        return null;
    }

}
